import 'dart:convert';
import 'package:dio/dio.dart';
import 'package:online_bookstore/constants/app_constants.dart';
import 'package:online_bookstore/models/book.dart';
import 'package:online_bookstore/models/user.dart';
import 'package:online_bookstore/models/order.dart';
import 'package:online_bookstore/services/mock_data_service.dart';

class ApiService {
  static final ApiService _instance = ApiService._internal();
  factory ApiService() => _instance;
  ApiService._internal();

  late final Dio _dio;

  void initialize() {
    _dio = Dio(BaseOptions(
      baseUrl: '${AppConstants.baseUrl}/${AppConstants.apiVersion}',
      connectTimeout: AppConstants.apiTimeout,
      receiveTimeout: AppConstants.apiTimeout,
      headers: {
        'Content-Type': 'application/json',
        'Accept': 'application/json',
      },
    ));

    // Add interceptors
    _dio.interceptors.add(LogInterceptor(
      requestBody: true,
      responseBody: true,
      logPrint: (object) => print(object),
    ));

    _dio.interceptors.add(InterceptorsWrapper(
      onRequest: (options, handler) {
        // Add auth token if available
        // final token = await _getAuthToken();
        // if (token != null) {
        //   options.headers['Authorization'] = 'Bearer $token';
        // }
        handler.next(options);
      },
      onError: (error, handler) {
        // Handle common errors
        if (error.response?.statusCode == 401) {
          // Handle unauthorized
        }
        handler.next(error);
      },
    ));
  }

  // Books API
  Future<List<Book>> getBooks({
    int page = 1,
    int limit = AppConstants.defaultPageSize,
    String? category,
    String? search,
    String? sortBy,
    String? sortOrder = 'asc',
  }) async {
    // Simulate network delay
    await Future.delayed(const Duration(milliseconds: 500));
    
    try {
      List<Book> books = MockDataService.mockBooks;
      
      // Apply filters
      if (category != null && category != 'All') {
        books = books.where((book) => book.category == category).toList();
      }
      
      if (search != null && search.isNotEmpty) {
        books = MockDataService.searchBooks(search);
      }
      
      // Apply sorting
      if (sortBy != null) {
        books.sort((a, b) {
          int comparison = 0;
          switch (sortBy) {
            case 'title':
              comparison = a.title.compareTo(b.title);
              break;
            case 'price':
              comparison = a.price.compareTo(b.price);
              break;
            case 'rating':
              comparison = a.rating.compareTo(b.rating);
              break;
            case 'published_date':
              comparison = a.publishedDate.compareTo(b.publishedDate);
              break;
          }
          return sortOrder == 'desc' ? -comparison : comparison;
        });
      }
      
      // Apply pagination
      final startIndex = (page - 1) * limit;
      final endIndex = startIndex + limit;
      if (startIndex >= books.length) {
        return [];
      }
      
      return books.sublist(
        startIndex,
        endIndex > books.length ? books.length : endIndex,
      );
    } catch (e) {
      throw e.toString();
    }
  }

  Future<Book> getBookById(String id) async {
    await Future.delayed(const Duration(milliseconds: 300));
    
    try {
      final book = MockDataService.getBookById(id);
      if (book == null) {
        throw 'Book not found';
      }
      return book;
    } catch (e) {
      throw e.toString();
    }
  }

  Future<List<Book>> getFeaturedBooks() async {
    await Future.delayed(const Duration(milliseconds: 400));
    
    try {
      return MockDataService.getFeaturedBooks();
    } catch (e) {
      throw e.toString();
    }
  }

  Future<List<Book>> getBooksByCategory(String category) async {
    await Future.delayed(const Duration(milliseconds: 400));
    
    try {
      return MockDataService.getBooksByCategory(category);
    } catch (e) {
      throw e.toString();
    }
  }

  Future<List<String>> getCategories() async {
    await Future.delayed(const Duration(milliseconds: 200));
    
    try {
      return MockDataService.mockCategories;
    } catch (e) {
      throw e.toString();
    }
  }

  // User API
  Future<User> login(String email, String password) async {
    await Future.delayed(const Duration(milliseconds: 800));
    
    try {
      // Simple mock authentication
      if (email == 'test@example.com' && password == 'password') {
        return MockDataService.mockUser;
      } else if (email == 'admin@example.com' && password == 'admin123') {
        return MockDataService.mockUser.copyWith(
          email: 'admin@example.com',
          name: 'Admin User',
        );
      } else {
        throw 'Invalid email or password';
      }
    } catch (e) {
      throw e.toString();
    }
  }

  Future<User> register({
    required String email,
    required String password,
    required String name,
    String? phoneNumber,
  }) async {
    await Future.delayed(const Duration(milliseconds: 1000));
    
    try {
      // Simple mock registration
      if (email == 'test@example.com') {
        throw 'Email already exists';
      }
      
      return MockDataService.mockUser.copyWith(
        email: email,
        name: name,
        phoneNumber: phoneNumber,
        createdAt: DateTime.now(),
        lastLoginAt: DateTime.now(),
      );
    } catch (e) {
      throw e.toString();
    }
  }

  Future<User> getUserProfile() async {
    try {
      final response = await _dio.get('/user/profile');
      return User.fromJson(response.data['data']);
    } on DioException catch (e) {
      throw _handleDioError(e);
    }
  }

  Future<User> updateUserProfile(Map<String, dynamic> updates) async {
    try {
      final response = await _dio.put('/user/profile', data: updates);
      return User.fromJson(response.data['data']);
    } on DioException catch (e) {
      throw _handleDioError(e);
    }
  }

  // Orders API
  Future<Order> createOrder({
    required List<Map<String, dynamic>> items,
    required Map<String, dynamic> shippingAddress,
    Map<String, dynamic>? billingAddress,
    String? notes,
  }) async {
    try {
      final response = await _dio.post('/orders', data: {
        'items': items,
        'shipping_address': shippingAddress,
        if (billingAddress != null) 'billing_address': billingAddress,
        if (notes != null) 'notes': notes,
      });
      return Order.fromJson(response.data['data']);
    } on DioException catch (e) {
      throw _handleDioError(e);
    }
  }

  Future<List<Order>> getUserOrders() async {
    try {
      final response = await _dio.get('/user/orders');
      final List<dynamic> ordersJson = response.data['data'];
      return ordersJson.map((json) => Order.fromJson(json)).toList();
    } on DioException catch (e) {
      throw _handleDioError(e);
    }
  }

  Future<Order> getOrderById(String orderId) async {
    try {
      final response = await _dio.get('/orders/$orderId');
      return Order.fromJson(response.data['data']);
    } on DioException catch (e) {
      throw _handleDioError(e);
    }
  }

  Future<Order> updateOrderStatus(String orderId, String status) async {
    try {
      final response = await _dio.put('/orders/$orderId/status', data: {
        'status': status,
      });
      return Order.fromJson(response.data['data']);
    } on DioException catch (e) {
      throw _handleDioError(e);
    }
  }

  // Error handling
  String _handleDioError(DioException error) {
    switch (error.type) {
      case DioExceptionType.connectionTimeout:
      case DioExceptionType.sendTimeout:
      case DioExceptionType.receiveTimeout:
        return AppConstants.networkError;
      case DioExceptionType.badResponse:
        final statusCode = error.response?.statusCode;
        if (statusCode == 500) {
          return AppConstants.serverError;
        } else if (statusCode == 404) {
          return AppConstants.noDataFound;
        }
        return error.response?.data['message'] ?? AppConstants.unknownError;
      case DioExceptionType.cancel:
        return 'Request cancelled';
      case DioExceptionType.unknown:
      default:
        return AppConstants.unknownError;
    }
  }
}
